Skip to content

feat: Span Streaming (WIP)#19119

Draft
Lms24 wants to merge 6 commits intodevelopfrom
lms/feat-span-first
Draft

feat: Span Streaming (WIP)#19119
Lms24 wants to merge 6 commits intodevelopfrom
lms/feat-span-first

Conversation

@Lms24
Copy link
Member

@Lms24 Lms24 commented Feb 2, 2026

This PR introduces span v2 types as defined in our [develop
spec](https://develop.sentry.dev/sdk/telemetry/spans/span-protocol/):

* Envelope types:
* `SpanV2Envelope`, `SpanV2EnvelopeHeaders`, `SpanContainerItem`,
`SpanContainerItemHeaders`
* Span v2 types:
* `SpanV2JSON` the equivalent to today's `SpanJSON`. Users will interact
with spans in this format in `beforeSendSpan`. SDK integrations will use
this format in `processSpan` (and related) hooks.
* `SerializedSpan` the final, serialized format for v2 spans, sent in
the envelope container item.

Closes #19101 (added automatically)

ref #17836
@Lms24 Lms24 changed the title feat(core): Add span v2 and envelope type definitions (#19100) feat: Span Streaming (WIP) Feb 2, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 2, 2026

Codecov Results 📊


Generated by Codecov Action

@github-actions
Copy link
Contributor

github-actions bot commented Feb 2, 2026

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

Scenario Requests/s % of Baseline Prev. Requests/s Change %
GET Baseline 8,631 - 8,968 -4%
GET With Sentry 1,600 19% 1,736 -8%
GET With Sentry (error only) 5,997 69% 6,029 -1%
POST Baseline 1,181 - 1,202 -2%
POST With Sentry 568 48% 585 -3%
POST With Sentry (error only) 1,032 87% 1,062 -3%
MYSQL Baseline 3,303 - 3,277 +1%
MYSQL With Sentry 417 13% 500 -17%
MYSQL With Sentry (error only) 2,659 81% 2,659 -

View base workflow run

@Lms24 Lms24 self-assigned this Feb 2, 2026
Lms24 and others added 3 commits February 4, 2026 14:25
…ility utilities (#19120)

This adds the foundation for user-facing span streaming configuration:

- **`traceLifecycle` option**: New option in `ClientOptions` that
controls whether spans are sent statically (when the entire local span
tree is complete) or streamed (in batches following interval- and
action-based triggers).

Because the span JSON will look different for streamed spans vs. static
spans (i.e. our current ones, we also need some helpers for
`beforeSendSpan` where users consume and interact with
`StreamedSpanJSON`:

- **`withStreamedSpan()` utility**: Wrapper function that marks a
`beforeSendSpan` callback as compatible with the streamed span format
(`StreamedSpanJSON`)

- **`isStreamedBeforeSendSpanCallback()` type guard**: Internal utility
to check if a callback was wrapped with `withStreamedSpan`
Adds a utility to create a span v2 envelope from a `SerializedSpan`
array + tests.

Note: I think here, the "v2" naming makes more sense than the
`StreamSpan` patter we use for user-facing functionality. This function
should never be called by users, and the envelope is type `span` with
content type `span.v2+json`

ref #17836
This PR adds span JSON conversion and serialization helpers for span
streaming:

* `spanToStreamedSpanJSON`: Converts a `Span` instance to a JSON object
used as intermediate representation as outlined in
#19100
* Adds `SentrySpan::getStreamedSpanJSON` method to convert our own spans
  * Directly converts any OTel spans
  * This is analogous to how `spanToJSON` works today.
* `spanJsonToSerializedSpan`: Converts a `StreamedSpanJSON` into the
final `SerializedSpan` to be sent to Sentry.

This PR also adds unit tests for both helpers.

ref #17836

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Jan Peer Stöcklmair <jan.peer@sentry.io>
Lms24 added 2 commits February 6, 2026 15:08
This PR adds the `captureSpan` pipeline, which takes a `Span` instance,
processes it and ultimately returns a `SerializedStreamedSpan` which can
then be enqueued into the span buffer.

ref #17836
This PR adds a simple span buffer implementation to be used for
buffering streamed spans.

Behaviour:
- buckets incoming spans by `traceId`, as we must not mix up spans of
different traces in one envelope
- flushes the entire buffer every 5s by default
- flushes the specific trace bucket if the max span limit (1000) is
reached. Relay accepts at max. 1000 spans per envelope
- computes the DSC when flushing the first span of a trace. This is the
latest time we can do it as once we flushed we have to freeze the DSC
for Dynamic Sampling consistency
- debounces the flush interval whenever we flush
- flushes the entire buffer if `Sentry.flush()` is called
- shuts down the interval-based flushing when `Sentry.close()` is called
- [implicit] Client report generation for dropped envelopes is handled
in the transport

Methods:
- `add` accepts a new span to be enqueued into the buffer
- `drain` flushes the entire buffer
- `flush(traceId)` flushes a specific traceId bucket. This can be used
by e.g. the browser span streaming implementation to flush out the trace
of a segment span directly once it ends.

Options:
- `maxSpanLimit` - allows to configure a 0 < maxSpanLimit < 1000 custom
span limit. Useful for testing but we could also expose this to users if
we see a need
- `flushInterval`- allows to configure a >0 flush interval

Limitations/edge cases:
- No maximum limit of concurrently buffered traces. I'd tend to accept
this for now and see where this leads us in terms of memory pressure but
at the end of the day, the interval based flushing, in combination with
our promise buffer _should_ avoid an ever-growing map of trace buckets.
Happy to change this if reviewers have strong opinions or I'm missing
something important!
- There's no priority based scheduling relative to other telemetry
items. Just like with our other log and metric buffers.
- since `Map` is [insertion order
preserving](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#description),
we apply a FIFO strategy when`drain`ing the trace buckets. This is in
line with our [develop
spec](https://develop.sentry.dev/sdk/telemetry/telemetry-processor/backend-telemetry-processor/#:~:text=The%20span%20buffer,in%20the%20buffer.)
for the telemetry processor but might lead to cases where new traces are
dropped by the promise buffer if a lof of concurrently running traces
are flushed. I think that's a fine trade off.

ref #19119
@github-actions
Copy link
Contributor

github-actions bot commented Feb 9, 2026

size-limit report 📦

Path Size % Change Change
@sentry/browser 25.59 kB +0.23% +58 B 🔺
⛔️ @sentry/browser - with treeshaking flags (max: 24.1 kB) 24.1 kB +0.21% +49 B 🔺
@sentry/browser (incl. Tracing) 42.57 kB +0.51% +213 B 🔺
@sentry/browser (incl. Tracing, Profiling) 47.23 kB +0.45% +208 B 🔺
@sentry/browser (incl. Tracing, Replay) 81.26 kB +0.32% +254 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 70.86 kB +0.31% +213 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 85.96 kB +0.3% +249 B 🔺
⛔️ @sentry/browser (incl. Tracing, Replay, Feedback) (max: 98 kB) 98.1 kB +0.22% +206 B 🔺
@sentry/browser (incl. Feedback) 42.31 kB +0.11% +43 B 🔺
@sentry/browser (incl. sendFeedback) 30.27 kB +0.18% +54 B 🔺
@sentry/browser (incl. FeedbackAsync) 35.27 kB +0.18% +61 B 🔺
@sentry/browser (incl. Metrics) 26.77 kB +0.2% +52 B 🔺
@sentry/browser (incl. Logs) 26.91 kB +0.21% +56 B 🔺
@sentry/browser (incl. Metrics & Logs) 27.58 kB +0.2% +55 B 🔺
@sentry/react 27.35 kB +0.18% +49 B 🔺
@sentry/react (incl. Tracing) 44.88 kB +0.45% +200 B 🔺
@sentry/vue 30.19 kB +0.7% +207 B 🔺
@sentry/vue (incl. Tracing) 44.4 kB +0.47% +205 B 🔺
@sentry/svelte 25.62 kB +0.27% +68 B 🔺
CDN Bundle 28.12 kB +0.15% +41 B 🔺
CDN Bundle (incl. Tracing) 43.36 kB +0.49% +209 B 🔺
CDN Bundle (incl. Logs, Metrics) 28.96 kB +0.14% +38 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) 44.21 kB +0.51% +223 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 67.92 kB +0.09% +55 B 🔺
⛔️ CDN Bundle (incl. Tracing, Replay) (max: 80 kB) 80.09 kB +0.24% +185 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 80.99 kB +0.28% +220 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 85.55 kB +0.26% +217 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 86.45 kB +0.25% +215 B 🔺
CDN Bundle - uncompressed 82.23 kB +0.14% +108 B 🔺
⛔️ CDN Bundle (incl. Tracing) - uncompressed (max: 128 kB) 128.4 kB +0.45% +569 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 85.06 kB +0.13% +108 B 🔺
⛔️ CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed (max: 131 kB) 131.23 kB +0.44% +569 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 208.44 kB +0.06% +108 B 🔺
⛔️ CDN Bundle (incl. Tracing, Replay) - uncompressed (max: 245 kB) 245 kB +0.24% +569 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 247.82 kB +0.24% +569 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 257.8 kB +0.23% +570 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 260.61 kB +0.22% +570 B 🔺
@sentry/nextjs (client) 47.23 kB +0.45% +209 B 🔺
⛔️ @sentry/sveltekit (client) (max: 43 kB) 43.01 kB +0.54% +229 B 🔺
@sentry/node-core 52.23 kB +0.09% +43 B 🔺
@sentry/node 166.52 kB +0.13% +201 B 🔺
@sentry/node - without tracing 94.02 kB +0.06% +49 B 🔺
@sentry/aws-serverless 109.52 kB +0.05% +50 B 🔺

View base workflow run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Span Streaming Implementation

1 participant